#pragma once

#include <deque>
#include <iosfwd>
#include <string>
#include "basetypes.h"

class GreatInt;
class FastUInt;

class GreatUInt
{
public:
	//ctors and copy
	explicit GreatUInt(const UInt i_num = 0);
	explicit GreatUInt(const char* i_string);
	explicit GreatUInt(const std::string& i_string);
	explicit GreatUInt(const FastUInt& i_that);
	GreatUInt(const GreatUInt& i_that);
	GreatUInt& operator=(const GreatUInt& i_that);
	GreatUInt& operator=(const UInt i_unm);

	//Accessors
	UInt ToUInt() const; //undefined if the actual value is bigger than maximum UInt
	void ToString(std::string& o_string)  const;
	void ToGreatInt(GreatInt& o_greatInt) const;
	void ToFastUInt(FastUInt& o_fastUInt) const;
	bool IsZero() const;
	bool IsUInt() const;
	bool IsInvalid() const;
	bool IsLessThan(const GreatUInt& i_that) const;
	UInt GetNumDigits() const;
	UInt GetDigit(const UInt i_digitNum) const;
	UInt DigitSum() const;

	//modifiers
	GreatUInt& Assign(const UInt i_num);
	GreatUInt& Assign(const char* i_string);
	GreatUInt& Assign(const std::string& i_string);
	GreatUInt& Assign(const FastUInt& i_fastUInt);
	GreatUInt operator++(int);
	GreatUInt operator--(int);
	GreatUInt& operator++();
	GreatUInt& operator--();
	GreatUInt& operator+=(const GreatUInt& i_that);
	GreatUInt& operator+=(const UInt i_num);
	GreatUInt& operator-=(const GreatUInt& i_that);
	GreatUInt& operator-=(const UInt i_num);
	GreatUInt& operator*=(const GreatUInt& i_that);
	GreatUInt& operator*=(const UInt i_factor);
	GreatUInt& operator/=(const GreatUInt& i_that);
	GreatUInt& operator%=(const GreatUInt& i_that);
	GreatUInt& operator&=(const GreatUInt& i_that);
	GreatUInt& operator|=(const GreatUInt& i_that);
	GreatUInt& operator^=(const GreatUInt& i_that);
	GreatUInt& operator<<=(const UInt i_factor);
	GreatUInt& operator>>=(const UInt i_factor);
	GreatUInt& ApplyExp(const UInt i_exp);
	GreatUInt& SetZero();
	GreatUInt& SetOne();
	GreatUInt& SetInvalid();
	void Swap(GreatUInt& i_that);

	// Arguments are read-only operands, result is saved in *this.
	GreatUInt& Add(const GreatUInt& i_first, const GreatUInt& i_second);
	GreatUInt& Sub(const GreatUInt& i_first, const GreatUInt& i_second);
	GreatUInt& Mul(const GreatUInt& i_first, const GreatUInt& i_second);
	GreatUInt& BitAnd(const GreatUInt& i_first, const GreatUInt& i_second);
	GreatUInt& BitOr(const GreatUInt& i_first, const GreatUInt& i_second);
	GreatUInt& BitXor(const GreatUInt& i_first, const GreatUInt& i_second);
	GreatUInt& DivWithRem(const GreatUInt& i_toBeDivided, const GreatUInt& i_divisor, bool i_storeQuotient = true, GreatUInt* o_other = 0);

	//friends
	friend std::ostream& operator<<(std::ostream& os, const GreatUInt& i_data);
	friend UInt operator%(const GreatUInt& i_greatUInt, UInt i_factor);
	friend bool operator==(const GreatUInt& i_left, const GreatUInt& i_right);
	friend class FastUInt;

private:
	typedef std::deque<UInt> Container;
	static const UInt MAX_ELEMENT_VALUE = 1000000000;
	static const UInt64 MAX_ELEMENT_VALUE_64 = MAX_ELEMENT_VALUE;
	static const UInt ALL_NINES = MAX_ELEMENT_VALUE-1;

private:
	Container m_data;
};

//non members
const GreatUInt operator+(const GreatUInt& i_left, const GreatUInt& i_right);
const GreatUInt operator-(const GreatUInt& i_left, const GreatUInt& i_right);
const GreatUInt operator*(const GreatUInt& i_greatUInt, UInt i_factor);
const GreatUInt operator*(UInt i_factor, const GreatUInt& i_greatUInt);
const GreatUInt operator*(const GreatUInt& i_left, const GreatUInt& i_right);
const GreatUInt operator/(const GreatUInt& i_left, const GreatUInt& i_right);
const GreatUInt operator%(const GreatUInt& i_left, const GreatUInt& i_right);
const GreatUInt operator&(const GreatUInt& i_left, const GreatUInt& i_right);
const GreatUInt operator|(const GreatUInt& i_left, const GreatUInt& i_right);
const GreatUInt operator^(const GreatUInt& i_left, const GreatUInt& i_right);
bool operator<(const GreatUInt& i_left, const GreatUInt& i_right);
bool operator<=(const GreatUInt& i_left, const GreatUInt& i_right);
bool operator>(const GreatUInt& i_left, const GreatUInt& i_right);
bool operator>=(const GreatUInt& i_left, const GreatUInt& i_right);
bool operator!=(const GreatUInt& i_left, const GreatUInt& i_right);
const GreatUInt Exp(const GreatUInt& i_base, const UInt i_exp);

//friends
std::ostream& operator<<(std::ostream& os, const GreatUInt& i_data);
UInt operator%(const GreatUInt& i_greatUInt, UInt i_factor);
bool operator==(const GreatUInt& i_left, const GreatUInt& i_right);

#include "GreatUInt.inl"